// src/pages/test/useMemoTest/index.tsx - useMemo Hook 详解页面
import React, { useState, useMemo } from 'react';
import { Card, Button, Space, Typography, Divider, InputNumber, List } from 'antd';

const { Title, Paragraph } = Typography;

/**
 * 模拟一个昂贵的计算函数
 * @param num - 输入数字
 * @returns 计算结果
 */
const expensiveCalculation = (num: number): number => {
  console.log('执行昂贵的计算...', num);
  // 模拟一个耗时的操作
  let result = 0;
  for (let i = 0; i < 10000000; i++) {
    result += num;
  }
  return result;
};

/**
 * 子组件 - 用户列表展示组件
 * @param props - 组件属性
 * @param props.users - 用户列表
 */
const UserList: React.FC<{ users: Array<{id: number, name: string}> }> = ({ users }) => {
  console.log('UserList 组件重新渲染');
  
  return (
    <div style={{ padding: '10px', border: '1px solid #ddd', margin: '5px', borderRadius: '4px' }}>
      <h3>用户列表:</h3>
      <List
        dataSource={users}
        renderItem={user => (
          <List.Item>
            {user.name} (ID: {user.id})
          </List.Item>
        )}
      />
    </div>
  );
};

const UseMemoTestPage: React.FC = () => {
  // 计数器状态
  const [count, setCount] = useState<number>(0);
  
  // 用户列表状态
  const [users, setUsers] = useState<Array<{id: number, name: string}>>([
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 3, name: 'Charlie' }
  ]);
  
  // 数字输入状态
  const [number, setNumber] = useState<number>(5);
  
  // 没有使用 useMemo 的昂贵计算
  const expensiveValueWithoutMemo = expensiveCalculation(number);
  
  // 使用 useMemo 缓存昂贵计算的结果
  const expensiveValueWithMemo = useMemo(() => {
    return expensiveCalculation(number);
  }, [number]); // 只有当 number 改变时才重新计算
  
  // 没有使用 useMemo 的用户列表处理
  const userListWithoutMemo = users.filter(user => user.id > 0);
  
  // 使用 useMemo 缓存用户列表处理结果
  const userListWithMemo = useMemo(() => {
    console.log('执行用户列表过滤计算');
    return users.filter(user => user.id > 0);
  }, [users]); // 只有当 users 改变时才重新计算
  
  /**
   * 添加用户的功能
   */
  const handleAddUser = () => {
    const newUser = {
      id: Date.now(), // 简单地使用时间戳作为 ID
      name: `User ${users.length + 1}`
    };
    setUsers([...users, newUser]);
  };
  
  return (
    <Card title="useMemo Hook 详解" style={{ minHeight: '100%' }}>
      <Typography>
        <Title level={2}>useMemo Hook 简介</Title>
        <Paragraph>
          useMemo 是 React 提供的一个 Hook，它用于优化性能，通过记忆计算结果的方式来防止在每次渲染时重复执行昂贵的计算。
          useMemo 会在依赖项发生变化时才重新计算并缓存新的值。
        </Paragraph>
        
        <Title level={3}>基础语法</Title>
        <Paragraph>
          <pre>
            {`const memoizedValue = useMemo(() => {\n  return expensiveCalculation(a, b);\n}, [a, b]);`}
          </pre>
        </Paragraph>
        <Paragraph>
          <ul>
            <li><code>memoizedValue</code> - 被记忆的计算结果</li>
            <li><code>useMemo</code> - React Hook</li>
            <li><code>{`() => { ... }`}</code> - 创建值的函数</li>
              <li><code>[a, b]</code> - 依赖数组，只有当依赖项发生变化时才会重新计算</li>
          </ul>
        </Paragraph>
        
        <Title level={3}>为什么需要 useMemo？</Title>
        <Paragraph>
          在 React 中，组件每次重新渲染时，其中的所有代码都会重新执行，包括函数调用和表达式计算。
          如果组件中包含一些计算成本很高的操作，这可能会导致性能问题。
        </Paragraph>
        <Paragraph>
          useMemo 允许你将这些昂贵的计算结果缓存起来，在依赖项没有变化的情况下直接返回缓存的结果，
          而不需要重新计算，从而提升组件的性能。
        </Paragraph>
      </Typography>
      
      <Divider />
      
      {/* 计数器示例 */}
      <Title level={4}>1. 计数器示例</Title>
      <Space direction="vertical" style={{ width: '100%' }}>
        <Paragraph>
          当前计数: <strong>{count}</strong>
        </Paragraph>
        <Space>
          <Button onClick={() => setCount(count + 1)}>
            增加计数
          </Button>
        </Space>
        <Paragraph>
          <Typography.Text type="secondary">
            每次点击增加计数按钮时，整个组件会重新渲染。注意观察控制台输出，看看 expensiveCalculation 函数的执行情况。
          </Typography.Text>
        </Paragraph>
      </Space>
      
      <Divider />
      
      {/* 昂贵计算示例 */}
      <Title level={4}>2. 昂贵计算示例</Title>
      <Space direction="vertical" style={{ width: '100%' }}>
        <Paragraph>
          输入数字: 
          <InputNumber 
            min={1} 
            max={100} 
            value={number} 
            onChange={(value) => setNumber(value || 1)} 
          />
        </Paragraph>
        <Paragraph>
          没有使用 useMemo 的计算结果: <strong>{expensiveValueWithoutMemo}</strong>
        </Paragraph>
        <Paragraph>
          使用 useMemo 的计算结果: <strong>{expensiveValueWithMemo}</strong>
        </Paragraph>
        <Paragraph>
          <Typography.Text type="secondary">
            观察控制台输出，可以看到当我们改变数字时 expensiveCalculation 函数会执行，
            但当我们点击增加计数按钮时（不改变数字），使用 useMemo 的版本不会重新执行计算。
          </Typography.Text>
        </Paragraph>
      </Space>
      
      <Divider />
      
      {/* 用户列表示例 */}
      <Title level={4}>3. 用户列表示例</Title>
      <Space direction="vertical" style={{ width: '100%' }}>
        <Space>
          <Button onClick={handleAddUser}>
            添加用户
          </Button>
        </Space>
        
        <Paragraph>
          没有使用 useMemo 的用户列表处理结果:
        </Paragraph>
        <UserList users={userListWithoutMemo} />
        
        <Paragraph>
          使用 useMemo 的用户列表处理结果:
        </Paragraph>
        <UserList users={userListWithMemo} />
        
        <Paragraph>
          <Typography.Text type="secondary">
            在这个例子中，UserList 组件会显示在控制台中重新渲染的信息。
            当我们点击增加计数按钮时，即使用户列表没有变化，没有使用 useMemo 的版本也会导致 UserList 重新渲染，
            而使用 useMemo 的版本则不会，因为引用是相同的。
          </Typography.Text>
        </Paragraph>
      </Space>
      
      <Divider />
      
      <Title level={3}>何时使用 useMemo？</Title>
      <Paragraph>
        <ul>
          <li>
            <strong>昂贵的计算：</strong>
            当组件中包含计算成本很高的操作（如复杂计算、大数据处理等）时。
          </li>
          <li>
            <strong>保持引用相等：</strong>
            当需要确保对象或数组在每次渲染时保持相同的引用，以避免子组件不必要的重新渲染时。
          </li>
          <li>
            <strong>依赖项比较：</strong>
            当需要确保传递给子组件的复杂对象在依赖项未变化时不触发子组件重新渲染时。
          </li>
        </ul>
      </Paragraph>
      
      <Title level={3}>注意事项</Title>
      <Paragraph>
        <ul>
          <li>
            <strong>不要过早优化：</strong>
            不是所有计算都需要使用 useMemo。只有在确实存在性能问题时才考虑使用。
          </li>
          <li>
            <strong>正确指定依赖项：</strong>
            依赖数组必须包含函数中使用的所有变量，否则可能会导致 bug。
          </li>
          <li>
            <strong>开销权衡：</strong>
            useMemo 本身也有开销，对于简单的计算可能得不偿失。
          </li>
          <li>
            <strong>useMemo 不能阻止每次渲染时的函数执行：</strong>
            useMemo 只是缓存计算结果，它不会阻止组件的重新渲染。
          </li>
        </ul>
      </Paragraph>
    </Card>
  );
};

export default UseMemoTestPage;